using System.Collections.Generic;
using Dimebrain.TweetSharp;
using Dimebrain.TweetSharp.Extensions;
using Dimebrain.TweetSharp.Fluent;
using Dimebrain.TweetSharp.Model;
using KlearTweet.Client.Infrastructure;

namespace KlearTweet.Client.WebServices
{
    public class TwitterSearch
    {
        private long _maxId;
        private int _nextPage;
        private bool _isBusy;
        private bool _reachedLastPage;
        private readonly System.Func<long, int, ITwitterLeafNode> _provider;

        public event EntityEventHandler SearchCompleted;


        public TwitterSearch(System.Func<long, int, ITwitterLeafNode> provider)
        {
            _maxId = -1;
            _nextPage = 1;
            _provider = provider;
        }


        public void RequestRefresh()
        {
            if (_isBusy) // TODO: It must be possible to refresh while asking for the next page. Remove after checking that there won't be concurrency issues.
                return;
            if (_maxId < 0)
            {
                RequestNextPage(); // Nothing to refresh! // TODO: Implicitely calling RequestNextPage might cause troubles
                return;
            }

            // TODO: This refresh must return all new entities, since it doesn't ask for the next pages
            // (practically, it is unlikely/uncommon for a user to receive a lot of new entities in a short period)
            // A possible solution would be to do: if(entities.Count==EntitiesToTakePerPage) ask for the next pages
            var twitter = _provider(_maxId, -1);
            twitter.CallbackTo((s, e) => OnSearchCompleted(0, e.Response));
            twitter.RequestAsync();
            _isBusy = true;
        }


        public void RequestNextPage()
        {
            if (_isBusy || _reachedLastPage)
                return;

            var twitter = _provider(_maxId, _nextPage); // TODO: Since _maxId changes after a refresh, the _nextPage is no longer correct; this will result in duplicates (but it isn't a real problem since they are properly handled)
            twitter.CallbackTo((s, e) => OnSearchCompleted(_nextPage, e.Response));
            twitter.RequestAsync();
            _isBusy = true;
        }


        private void OnSearchCompleted(int page, string response)
        {
            _isBusy = false;
            IList<Model.IEntity> entities;
            try
            {
                var statuses = response.AsStatuses();
                entities = new List<Model.IEntity>();
                foreach (var status in statuses)
                {
                    entities.Add(new Model.Tweet
                    {
                        Id = status.Id,
                        ProfileImageUrl = status.User.ProfileImageUrl,
                        FromUserScreenName = status.User.ScreenName,
                        ToUserScreenName = status.InReplyToScreenName,
                        CreatedDate = status.CreatedDate,
                        Text = status.Text,
                    });
                    _maxId = System.Math.Max(_maxId, status.Id); // TODO: Is there a better solution?
                }
            }
            catch
            {
                try
                {
                    var r = response.AsSearchResult();
                    entities = new List<Model.IEntity>();
                    foreach (var status in r.Statuses)
                    {
                        entities.Add(new Model.Tweet
                        {
                            Id = status.Id,
                            ProfileImageUrl = status.ProfileImageUrl,
                            FromUserScreenName = status.FromUserScreenName,
                            ToUserScreenName = status.ToUserScreenName,
                            CreatedDate = status.CreatedDate,
                            Text = status.Text,
                        });
                        _maxId = System.Math.Max(_maxId, status.Id); // TODO: Is there a better solution?
                    }
                }
                catch
                {
                    entities = null; // Failed
                }
            }

            if (entities != null) // Succeeded
            {
                if (_nextPage == page) // Was retrieving the current page
                {
                    if (entities.Count == 0) // TODO: Confirm that it is indeed the case in all scenarios + that a refresh doesn't change anything
                        _reachedLastPage = true;
                    else
                        _nextPage++;
                }
            }

            var handler = SearchCompleted;
            if (handler != null)
                handler(this, new EntityEventArgs(entities));
        }
    }
}